<?php

/**
 * Конструктор SQL-запросов
 *
 * Собирает запросы на выборку одного или нескольких объектов определенного типа
 */
class ObjectQuery {
	
	private
		$oType           = null,
		$sFilters        = '',
		$objectId        = 0,
		$sSelect         = '',
		$aSelectFields   = array (),
		$sMainTableName  = '',
		$aJoins          = array (),
		$sJoins          = '',
		$str             = null;
	
	/**
	 * Конструктор
	 * @param Type $oType объект типа
	 * @param integer $objectId id объекта
	 */
	public function __construct (Type $oType, $objectId) {
		$this->oType = $oType;
		$this->objectId = $objectId;
	}
	
	/**
	 * Возвращает строку с SQl-запросом одиночного объекта, сгенерированным на основе полей типа
	 * @return string
	 */
	public function __toString () {
		if ($this->str !== null) {
			return $this->str;
		}
		
		//$cacheController = CacheController::getInstance ();
		
		if (is_array ($this->objectId)) {
			$sFilters = '`' . $this->oType->getTableName() . '`.`id` IN (\'' . implode("','", $this->objectId) . '\')';
		} else {
			$sFilters = '`' . $this->oType->getTableName() . '`.`id` = ' . $this->objectId;
		}
		
		$key = 'explay_objecy_query_' . $this->oType->getTableName() . '_' . md5($sFilters);
		
		/*$cacheParams = array (
						'key'      => $key,
						'lifetime' => CORE_LIFETIME,
						'dir'      => 'sql_queries'
					);
		
		if ($sqlString = $cacheController->getString ($cacheParams)) {
			return $sqlString;
		}*/
		
		if ($sqlString = CacheController::load($key, CORE_LIFETIME)) {
			return $sqlString;
		}
		
		$this->build ();
		
		$sqlString = "SELECT SQL_CACHE\r\n\t" . $this->sSelect . "\r\nFROM `" . $this->sMainTableName . '`' . $this->sJoins;
		if ($sFilters != '') {
			$sqlString .= "\r\nWHERE " . $sFilters;
		}
				
		//$cacheParams['value'] = $sqlString;
		//$cacheController->writeString ($cacheParams);
		
		CacheController::save($key, $sqlString, CORE_LIFETIME);
		
		return $this->str = $sqlString;
	}
	
	/**
	 * Подготавливает запрос
	 * @return void
	 */
	private function build () {
		$this->sMainTableName = $this->oType->getTableName ();
		
		// Если у типа нет виртуальных полей, то не расчитываем каждое поле а ставим *
		if ((count ($this->oType->getFieldsNames ()) - count ($this->oType->getRealFieldsNames ())) == 0) {
			$this->sSelect = ' * ';
			return;
		}
		
		$this->aSelectFields[] = '`' . $this->oType->getTableName() . '`.`id`';
		
		$aFields = $this->oType->getFields ();
		
		foreach ($aFields as $oField) {
			$this->renderField ($oField);
		}
		
		$sSelectFields = implode (",\r\n\t", $this->aSelectFields);
		
		foreach ($this->aJoins as $varName => $arr) {
			$this->sJoins .= "\r\nLEFT JOIN `" . $arr[0] . "` AS `" . $varName . "` ON ". $arr[1];
		}
		
		$this->sSelect = $sSelectFields;
	}
	
	/**
	 * Обрабатывает поле
	 * @param Field $oField информация и поле
	 * @return void
	 */
	private function renderField (Field $oField) {
		$fieldName = $oField->getName ();
		$tablePrefix = $this->sMainTableName;
		$character = $oField->getCharacter ();
		
		// Справочники пропускаем
		if ($character == 'guide' || $character == 'multiguide' || $character == 'tags') {
			return;
		}
		
		if ($character == 'object_value') {
			$typesController = TypesController::getInstance ();
			$fieldsController = FieldsController::getInstance ();
			
			if (!$connectionFieldId = $oField->getAddId ()) {
				return;
			}
			
			if (!$oChildField = $fieldsController->getField ($connectionFieldId)) {
				return;
			}
			
			// Очень странное поле...
			if (!$oChildField->isReal ()) {				
				if (!$oLastChildField =  $fieldsController->getField ($oChildField->getAddId ())) {
					return;
				}
				
				if (!$oLastChildField->isReal ()) {
					return;
				}
				
				$childTypeId = $oChildField->getTypeId ();
				$connectionId = $oField->getConnectionId ();
				
				$oChildType = $typesController->getTypeById ($childTypeId);
				
				$childTableName = $oChildType->getTableName ();
				
				$var = 'var' . $oField->getTypeId() . '_' . $childTypeId . '_' . $connectionId;
				
				// Пробуем найти и установить связь между типом $oField и типом, поля $oChildField
				if (!isset ($this->aJoins[$var])) {
					if (!$oTypesConnection = $typesController->getConnection ($connectionId)) {
						return;
					}
					
					if (!$oChildConectedField = $fieldsController->getField ($oTypesConnection->getValue ('child_rel_field_id'))) {
						return;
					}
					
					// ... ON ...
					if ($oTypesConnection->getValue ('parent_type_id') == $oField->getTypeId ()) {
						$this->aJoins[$var] = array ($childTableName, '`' . $tablePrefix . '`.`id` = `' . $var . '`.`' . $oChildConectedField->getName() . '`');
					} else {
						$this->aJoins[$var] = array ($childTableName, '`' . $tablePrefix . '`.`' . $oChildConectedField->getName() . '` = `' . $var . '`.`id`');
					}
				}
				
				// Подменяем переменные полей - теперь обработка пойдет относительно 2-го и 3-го дочерних типов
				$oField = $oChildField;
				$oChildField = $oLastChildField;
				$tablePrefix = $var;
			}
			
			$childTypeId = $oChildField->getTypeId ();
			$connectionId = $oField->getConnectionId ();
			$oChildType = $typesController->getTypeById ($childTypeId);
			
			$childTableName = $oChildType->getTableName ();
			
			$var = 'var' . $oField->getTypeId() . '_' . $childTypeId . '_' . $connectionId;
			
			// Добавляем поля к запросу
			if (isset ($this->aJoins[$var])) { // Если поля из этой таблицы уже вибирались
				$this->aSelectFields[] = '`' . $var . '`.`' . $oChildField->getName() . '` AS `' . $fieldName . '`';
			}
			else { // Поле из этой таблицы выбирается первый раз
				// Находим объект связи между типами (1-1)
				if (!$oTypesConnection = $typesController->getConnection ($connectionId)) {
					return;
				}
				
				$this->aSelectFields[] = '`' . $var . '`.`' . $oChildField->getName() . '` AS `' . $fieldName . '`';
				

				if (!$oChildConectedField = $fieldsController->getField ($oTypesConnection->getValue ('child_rel_field_id'))) {
					return;
				}
				
				// ... ON ...
				if ($oTypesConnection->getValue ('parent_type_id') == $oField->getTypeId ()) {
					$this->aJoins[$var] = array ($childTableName, '`' . $tablePrefix . '`.`id` = `' . $var . '`.`' . $oChildConectedField->getName() . '`');
				} else {
					$this->aJoins[$var] = array ($childTableName, '`' . $tablePrefix . '`.`' . $oChildConectedField->getName() . '` = `' . $var . '`.`id`');
				}
			}
		}
		else {
			$this->aSelectFields[] = '`'.$tablePrefix.'`.`' . $fieldName . '`';
		}
	}

}